// NFC_02 [OpenGL Colour Cube].nova



// Using namespace declarations.
using Library.Math;
using Library.NFC;
using Library.OpenGL;



// The application class.
class ColourCubeWindowApp : OpenGLWindow
{
   // Instance data members.
   private int mouseX, mouseY;

   private double spinX, spinY;

   private bool mouseButtonDown;

   private Matrix4d matrix;

   private uint cubeDisplayList;



   // Application class's "main" function.
   public static void main( String[] args )
   {
      // Create a new colour cube window.
      ColourCubeWindowApp window = new ColourCubeWindowApp( "NFC_02 [OpenGL Colour Cube]", 640, 480 );

      // Show the window.
      window.show( true );

      // Process the window's events.
      window.processEvents( );
   }



   // Window's constructor method.
   private ColourCubeWindowApp( String title, int sizeX, int sizeY )
      : OpenGLWindow( title, sizeX, sizeY )
   {
      // Initialize the window's data members.
      mouseX = mouseY = 0;
      spinX = spinY = 0;

      mouseButtonDown = false;

      matrix = new Matrix4d( );
      matrix.rotateX( 30.0 );
      matrix.rotateY( -45.0 );


      // Initialize OpenGL.
      OpenGL.glClearColor( 0.5f, 0.5f, 0.5f, 0.0f );
      OpenGL.glShadeModel( OpenGL.GL_SMOOTH );
      OpenGL.glClearDepth( 1.0 );
      OpenGL.glEnable( OpenGL.GL_DEPTH_TEST );

      // Generate a display list to draw a colour cube.
      cubeDisplayList = OpenGL.glGenLists( 1 );
      OpenGL.glNewList( cubeDisplayList, OpenGL.GL_COMPILE );
      ColourCube.drawColourCube( );
      OpenGL.glEndList( );
   }



   // On idle event handler.
   public virtual void onIdle( )
   {
      // Check if the mouse button is up.
      if ( mouseButtonDown == false )
      {
         // Spin the cube.
         rotateCube( );
      }

      // Paint the window.
      onPaint( );
   }



   // On mouse button down event handler.
   public virtual void onMouseButtonDown( int button )
   {
      // Check for the left mouse button index.
      if ( button == 0 )
      {
         // Update the state of the left mouse button.
         mouseButtonDown = true;

         // Stop the cube from spinning.
         spinX = spinY = 0;
      }
   }



   // On mouse button up event handler.
   public virtual void onMouseButtonUp( int button )
   {
      // Check for the left mouse button index.
      if ( button == 0 )
      {
         // Update the state of the left mouse button.
         mouseButtonDown = false;
      }
   }



   // On mouse move event handler.
   public virtual void onMouseMove( int posX, int posY )
   {
      // Update the state of the left mouse button.
      mouseButtonDown = getKeyState( KeyReference.VK_LBUTTON );

      // Check if the mouse button is pressed.
      if ( mouseButtonDown )
      {
         spinX = (double)( posY - mouseY ) / 2.0;
         spinY = (double)( posX - mouseX ) / 2.0;

         // Orientate the cube.
         rotateCube( );
      }

      mouseX = posX;
      mouseY = posY;
   }



   // On size event handler.
   public virtual void onSize( int sizeX, int sizeY )
   {
      // Prevent a divide by zero error.
      if ( sizeY == 0 )
      {
         // Set the height equal to one.
         sizeY = 1;
      }

      // Reset the current viewport.
      OpenGL.glViewport( 0, 0, sizeX, sizeY );

      // Select the projection matrix.
      OpenGL.glMatrixMode( OpenGL.GL_PROJECTION );
      // Reset the projection matrix.
      OpenGL.glLoadIdentity( );

      // Calculate the aspect ratio of the window.
      OpenGL.gluPerspective( 45.0, (double)sizeX / (double)sizeY, 0.1, 100.0 );

      // Select the modelview matrix.
      OpenGL.glMatrixMode( OpenGL.GL_MODELVIEW );
      // Reset the modelview matrix.
      OpenGL.glLoadIdentity( );
   }



   // On paint event handler.
   public virtual void onPaint( )
   {
      // Check if the window is minimized.
      if ( getState( ) == STATE_MINIMIZED )
      {
         // Sleep the current thread for ten milliseconds.
         Thread.sleep( 10 );

         // Return from the method.
         return;
      }


      // Clear the buffers.
      OpenGL.glClear( OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT );

      // Reset the current matrix.
      OpenGL.glLoadIdentity( );

      // Move into the scene.
      OpenGL.glTranslatef( 0.0f, 0.0f, -3.0f );

      // Rotate the colour cube.
      OpenGL.glMultMatrix( matrix );

      // Call the colour cube display list.
      OpenGL.glCallList( cubeDisplayList );

      // Swap the window's OpenGL buffers.
      swapBuffers( );
   }



   // Rotate the cube.
   private void rotateCube( )
   {
      // Create a local rotation matrix.
      Matrix4d rotationMatix = new Matrix4d( );

      // Rotate the rotation matrix.
      rotationMatix.rotateX( spinX );
      rotationMatix.rotateY( spinY );

      // Pre-multiply the cube's matrix with the rotation matrix.
      matrix = rotationMatix.multiply( matrix );
   }
}



// Class to draw a colour cube using OpenGL.
class ColourCube
{
   // Draw an OpenGL colour cube.
   public static void drawColourCube( )
   {
      // Draw the colour cube.
      OpenGL.glBegin( OpenGL.GL_QUAD_STRIP );
         OpenGL.glColor3f( 1.0f, 0.0f, 1.0f );     // Magenta.
         OpenGL.glVertex3f( -0.5f, 0.5f, 0.5f );   // Near, upper left.

         OpenGL.glColor3f( 1.0f, 0.0f, 0.0f );     // Red.
         OpenGL.glVertex3f( -0.5f, -0.5f, 0.5f );  // Near, lower left.

         OpenGL.glColor3f( 1.0f, 1.0f, 1.0f );     // White.
         OpenGL.glVertex3f( 0.5f, 0.5f, 0.5f );    // Near, upper right.

         OpenGL.glColor3f( 1.0f, 1.0f, 0.0f );     // Yellow.
         OpenGL.glVertex3f( 0.5f, -0.5f, 0.5f );   // Near, lower right.

         OpenGL.glColor3f( 0.0f, 1.0f, 1.0f );     // Cyan.
         OpenGL.glVertex3f( 0.5f, 0.5f, -0.5f );   // Far, upper right.

         OpenGL.glColor3f( 0.0f, 1.0f, 0.0f );     // Green.
         OpenGL.glVertex3f( 0.5f, -0.5f, -0.5f );  // Far, lower right.

         OpenGL.glColor3f( 0.0f, 0.0f, 1.0f );     // Blue.
         OpenGL.glVertex3f( -0.5f, 0.5f, -0.5f );  // Far, upper left.

         OpenGL.glColor3f( 0.0f, 0.0f, 0.0f );     // Black.
         OpenGL.glVertex3f( -0.5f, -0.5f, -0.5f ); // Far, lower left.

         OpenGL.glColor3f( 1.0f, 0.0f, 1.0f );     // Magenta.
         OpenGL.glVertex3f( -0.5f, 0.5f, 0.5f );   // Near, upper left.

         OpenGL.glColor3f( 1.0f, 0.0f, 0.0f );     // Red.
         OpenGL.glVertex3f( -0.5f, -0.5f, 0.5f );  // Near, lower left.
      OpenGL.glEnd( );

      OpenGL.glBegin( OpenGL.GL_QUADS );
         OpenGL.glColor3f( 1.0f, 0.0f, 1.0f );     // Magenta.
         OpenGL.glVertex3f( -0.5f, 0.5f, 0.5f );   // Near, upper left.

         OpenGL.glColor3f( 1.0f, 1.0f, 1.0f );     // White.
         OpenGL.glVertex3f( 0.5f, 0.5f, 0.5f );    // Near, upper right.

         OpenGL.glColor3f( 0.0f, 1.0f, 1.0f );     // Cyan.
         OpenGL.glVertex3f( 0.5f, 0.5f, -0.5f );   // Far, upper right.

         OpenGL.glColor3f( 0.0f, 0.0f, 1.0f );     // Blue.
         OpenGL.glVertex3f( -0.5f, 0.5f, -0.5f );  // Far, upper left.
      OpenGL.glEnd( );

      OpenGL.glBegin( OpenGL.GL_QUADS );
         OpenGL.glColor3f( 1.0f, 0.0f, 0.0f );     // Red.
         OpenGL.glVertex3f( -0.5f, -0.5f, 0.5f );  // Near, lower left.

         OpenGL.glColor3f( 1.0f, 1.0f, 0.0f );     // Yellow.
         OpenGL.glVertex3f( 0.5f, -0.5f, 0.5f );   // Near, lower right.

         OpenGL.glColor3f( 0.0f, 1.0f, 0.0f );     // Green.
         OpenGL.glVertex3f( 0.5f, -0.5f, -0.5f );  // Far, lower right.

         OpenGL.glColor3f( 0.0f, 0.0f, 0.0f );     // Black.
         OpenGL.glVertex3f( -0.5f, -0.5f, -0.5f ); // Far, lower left.
      OpenGL.glEnd( );
   }
}